/**
 * 
 */
package ro.dta.idbi.patterns;

import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import ro.dta.idbi.interfaces.IntegrationOperation;
import ro.dta.idbi.model.AbstractPattern;
import ro.dta.idbi.model.AbstractPrimitivePattern;
import ro.dta.idbi.model.Utils;
import uk.ac.ic.doc.automed.IntegrityException;
import uk.ac.ic.doc.automed.NotFoundException;
import uk.ac.ic.doc.automed.reps.Schema;
import uk.ac.ic.doc.automed.reps.SchemaObject;

/**
 * <b>Addition of one-to-many relationship transformation</b>
 * 
 * Example of use:<br />
 * <code>add_otm_rel (&lt;&lt;table1,col1&gt;&gt;, ..., &lt;&lt;table1,coln&gt;&gt;,&lt;&lt;table2,col1&gt;&gt;, ..., &lt;&lt;table2,coln&gt;&gt;)</code>
 * <br />
 * 
 * A foreign key constraint from <i>table1,col1</i> to <i>table2,col2</i> is
 * introduced
 * 
 * @author Tudor Dobrila
 * 
 */
public class AddRelationOTM extends AbstractPattern {

	/**
	 * Name of the pattern
	 */
	public static final String NAME = "Addition of One-To-Many Relationship";

	/**
	 * Description of the pattern
	 */
	public static final String DESCRIPTION = "Introduce a one-to-many relationship";

	/**
	 * Name of the command associated with the pattern
	 */
	public static final String COMMAND = "add_otm_rel";

	/**
	 * Integration phase during which the command is applied
	 */
	public static final EnumSet<IntegrationOperation> OPERATION = EnumSet
			.of(IntegrationOperation.MERGING);

	/**
	 * List of all columns in the foreign key
	 */
	private List<String> cols;

	/**
	 * Name of the source table
	 */
	private String sourceTable;

	/**
	 * Name of the target table
	 */
	private String targetTable;

	/**
	 * Name of the columns belonging to the source table
	 */
	private List<String> sourceCols;

	/**
	 * Name of the columns belonging to the target table
	 */
	private List<String> targetCols;

	/**
	 * Name of the newly created FK
	 */
	private String fkName;

	/**
	 * Addition of one-to-many relationship pattern class constructor
	 * 
	 * @param sourceSchema
	 *            Source schema over which the transformation is applied
	 */
	public AddRelationOTM(final Schema sourceSchema) throws NotFoundException, IntegrityException {
		super(sourceSchema);

		pattern = Pattern.compile("^" + COMMAND + "[ ]*\\((" + NAME_REGEX + ")" + COMMA_REGEX
				+ COL_SEQ_REGEX + "\\)$");
	}

	/**
	 * Addition of one-to-many relationship pattern class constructor
	 * 
	 * @param sourceSchema
	 *            Name of source schema over which the transformation is applied
	 */
	public AddRelationOTM(final String sourceSchema) throws NotFoundException, IntegrityException {
		this(Schema.getSchema(sourceSchema));
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see ro.dta.idbi.model.AbstractPattern#parse(java.lang.String)
	 */
	@Override
	protected void parse(String trans) {
		final Matcher matcher = pattern.matcher(trans);
		matcher.find();

		fkName = matcher.group(1);
		cols = AbstractPrimitivePattern.getColumns(matcher.group(2));
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see ro.dta.idbi.model.AbstractPattern#getPrimitiveCommands()
	 */
	@Override
	protected List<String> getPrimitiveCommands() throws NotFoundException, IntegrityException {
		List<String> result = new ArrayList<String>();
		StringBuilder cmd = new StringBuilder();

		// Step 1: create the source columns (if they don't exist)
		for (int i = 0; i < sourceCols.size(); i++) {
			String s = sourceCols.get(i);
			String t = targetCols.get(i);
			SchemaObject targetCol = sourceSchema.getSchemaObject("<<" + targetTable + "," + t
					+ ">>");
			Object[] targetDef = targetCol.getSchemeDefinition();

			cmd.setLength(0);
			cmd.append("extend (");
			cmd.append(Utils.genColRepresentation(sourceTable, s, targetDef[2], targetDef[3]));
			cmd.append(", Range Void Any)");
			result.add(cmd.toString());
		}

		// Step 2: create the FK constraint from source to target
		cmd.setLength(0);
		cmd.append("add (");
		cmd.append(Utils.genFKRepresentation(fkName, sourceTable, targetTable, sourceCols,
				targetCols));
		cmd.append(")");
		result.add(cmd.toString());

		return result;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see ro.dta.idbi.model.AbstractPattern#verify()
	 */
	@Override
	protected boolean verify() {
		if (cols.size() % 2 == 1) {
			return false;
		}

		sourceCols = new ArrayList<String>();
		targetCols = new ArrayList<String>();

		Map.Entry<String, String> firstCol = parseColName(cols.get(0));
		Map.Entry<String, String> midCol = parseColName(cols.get(cols.size() / 2));
		for (int i = 0; i < cols.size() / 2; i++) {
			Map.Entry<String, String> col1 = parseColName(cols.get(i));
			if (!col1.getKey().equals(firstCol.getKey())) {
				return false;
			}
			sourceCols.add(col1.getValue());

			Map.Entry<String, String> col2 = parseColName(cols.get(i + cols.size() / 2));
			if (!col2.getKey().equals(midCol.getKey())) {
				return false;
			}
			targetCols.add(col2.getValue());
		}

		sourceTable = firstCol.getKey();
		targetTable = midCol.getKey();

		return true;
	}

}
